{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "62549efe",
   "metadata": {
    "pycharm": {
     "name": "#%% md\n"
    }
   },
   "source": [
    "# The Language 😼\n",
    "\n",
    "We'll call `Kork`'s programming language the **Smirking Cat** (😼) or `Kork` or simply 😼. Honestly, it doesn't really matter (as long as the LLM doesn't get confused!).\n",
    "Set the language name in the prompt to whatever you'd like. Keep it short to avoid consuming too many tokens on things that don't matter. 🙃\n",
    "\n",
    "The grammar for the language is defined [here](https://github.com/langchain-ai/kork/blob/main/kork/parser.py).\n",
    "\n",
    "This language isn't particularly good and is extremely limited (especially at the moment) -- it was clobbered together\n",
    "in a few hours, is incorrect in some known ways (e.g., `const` isn't actually `const`) and most definitely incorrect in unknown ways as well. 🤫\n",
    "\n",
    "The goal is *NOT* to allow the LLM to write arbitrary python code. If you need arbitrary code execution use a real programming language.\n",
    "\n",
    "`Kork` is meant to accommodate scenarios in which one wants to produce small constrained programs that can help achieve some specific task phrased in natural language. \n",
    "\n",
    "It was designed with the following ideas in mind: \n",
    "\n",
    "* Keep the language minimal to limit what the LLM can code\n",
    "* Encourage the LLM to rely heavily on external function invocation\n",
    "* Keep the syntax similar to existing languages, so the LLM can learn how to code in it using information in the prompt\n",
    "* Discourage the LLM from assuming a standard library is available\n",
    "\n",
    "## Alternative Approaches\n",
    "\n",
    "* Changing syntax: syntax based on S-expressions looks promising based on a few qualitative experiments.\n",
    "* Implementing a light weight python / typescript interpreter in python that supports a minimal subset of features."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "79fd8e41",
   "metadata": {
    "collapsed": false,
    "jupyter": {
     "outputs_hidden": false
    },
    "nbsphinx": "hidden",
    "pycharm": {
     "name": "#%%\n"
    },
    "tags": [
     "remove_cell"
    ]
   },
   "outputs": [],
   "source": [
    "%load_ext autoreload\n",
    "%autoreload 2\n",
    "\n",
    "import sys\n",
    "\n",
    "sys.path.insert(0, \"../\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4fb92038-08ad-4cf6-91db-06fb25c281bd",
   "metadata": {
    "tags": []
   },
   "source": [
    "## The interpreter\n",
    "\n",
    "Let's take a look at the **Smirking Cat** interpreter."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "62630c9b-eb0b-49a3-9bb8-f7bdfd3846b3",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "from kork import run_interpreter"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "f0714c89-d220-44f1-a5f7-822d3434502a",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'environment': Environment(parent=None, variables={'x': 10}), 'errors': []}"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "result = run_interpreter(\"var x = 1; x = x * 10\")\n",
    "result"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f5a0586b-a6f3-4f03-8245-5e67b58f3ef3",
   "metadata": {},
   "source": [
    "The environment reflects the state of the global symbols after the interpreter finished executing the code."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "927549ac-c80b-4e71-bae2-27f5f75b30a0",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'x': 10}"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "result[\"environment\"].variables"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9e61c733-e1aa-4b01-ab8f-570274331770",
   "metadata": {},
   "source": [
    "Syntax errors will lead the `errors` key being populated. The current error isn't super informative."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "3f3e95a1-65a0-46fa-8538-8ef35eedbaf1",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'environment': Environment(parent=None, variables={}),\n",
       " 'errors': [lark.exceptions.UnexpectedToken()]}"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "run_interpreter(\"1 = 2\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b47e4f89-9734-475b-ac40-248430236cb7",
   "metadata": {},
   "source": [
    "RunTimeExceptions will also appear in the `errors` key."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "02b5723d-985d-4187-b5b3-048e192af59d",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'environment': Environment(parent=None, variables={}),\n",
       " 'errors': [kork.exceptions.KorkRunTimeException('Variable `x` not found')]}"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "run_interpreter(\"x + 1\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cc821fab-8b69-49f6-86fd-f1022215001b",
   "metadata": {},
   "source": [
    "## Environment\n",
    "\n",
    "The interpreter can be run with a pre-populated environment"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "66fbfc14-4d4c-4933-8780-5b8d287b087a",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "from kork import Environment"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "2f39a42e-1073-426b-ab02-994e9e710779",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "env = Environment()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "a76df2c1-772a-4f3f-a028-3d5ca2cb36b9",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "10"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "env.set_symbol(\"x\", 10)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "105a1633-4640-4bd1-9c9b-28643edfc9c4",
   "metadata": {},
   "source": [
    "Please note that `y` was created even though no `let` or `const` or `var` keyword was used."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "b357aba2-1b50-48e9-a350-23f4ecd9dcbe",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'environment': Environment(parent=None, variables={'x': 10, 'y': 30}),\n",
       " 'errors': []}"
      ]
     },
     "execution_count": 10,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "result = run_interpreter(\"y = x * 3\", environment=env)\n",
    "result"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "04a27351-3100-4c98-b8fa-9afabbb9da90",
   "metadata": {},
   "source": [
    "## Foreign Functions\n",
    "\n",
    "A user can import existing python functions as foreign functions."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "51ef8652-7e3a-4cae-8c28-39ed909c14b1",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "from kork.foreign_funcs import to_extern_func_def"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "id": "24577081-e906-4695-9df6-e06587a60a27",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "def foo(s: str) -> str:\n",
    "    \"\"\"Foo will reverse a string!\"\"\"\n",
    "    return s[::-1]"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4bccc8ec-33f1-400a-88df-19029b0ef207",
   "metadata": {},
   "source": [
    "Below we'll convert the python function into an internal representation of an external function definition."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "id": "50707464-64cd-4d5e-ba8a-7cebdd86a756",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "extern_func_def = to_extern_func_def(foo)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "id": "292e356a-fcb0-4694-ada6-6e561e4c22e4",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "ExternFunctionDef(name='foo', params=ParamList(params=[Param(name='s', type_='str')]), return_type='str', implementation=<function foo at 0x7f5e68873370>, doc_string='Foo will reverse a string!')"
      ]
     },
     "execution_count": 14,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "extern_func_def"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0c82a5d1-310d-4d49-b439-8290f7cd41e8",
   "metadata": {},
   "source": [
    "Let's add this function to the environment"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "id": "6de0af02-ad56-443c-b3f8-f9b1be87f5e7",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "ExternFunctionDef(name='foo', params=ParamList(params=[Param(name='s', type_='str')]), return_type='str', implementation=<function foo at 0x7f5e68873370>, doc_string='Foo will reverse a string!')"
      ]
     },
     "execution_count": 15,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "env.set_symbol(\"foo\", extern_func_def)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "id": "fef9f8e9-3254-4417-9170-3a995c7450e1",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'woem'"
      ]
     },
     "execution_count": 16,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "result = run_interpreter('var z = foo(\"meow\")', environment=env)\n",
    "\n",
    "result[\"environment\"].variables[\"z\"]"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.2"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
